package sg.edu.nus.iss.cerberus.engine;

import java.util.ArrayList;
import java.util.Iterator;
import sg.edu.nus.iss.cerberus.model.CClass;
import sg.edu.nus.iss.cerberus.model.CConstructor;
import sg.edu.nus.iss.cerberus.model.CMemberMethod;
import sg.edu.nus.iss.cerberus.model.MethodTestData;
import sg.edu.nus.iss.cerberus.model.TestGenerationData;
import sg.edu.nus.iss.cerberus.util.CerberusConstants;

/**
 * A test engine class for populating of class instances, local variables creation and method calls.
 * 
 * @author Priyanka
 * @author Lin
 * 
 */

public class TestEngine extends AssertionTestEngine{
	
	private CClass classobj=null;
	private CMemberMethod cmethod=null;
	private final static String fixtureobj="fixtureObj";
	private final static String resultobj="result";
	private final static String expected = "expected";
	private ArrayList<String> paramsList;
	private MethodTestData methodTestData;
	
	public TestEngine(CClass classobj) {
		
		super();
		this.classobj = classobj;
		this.methodTestData=null;
	}
	
	public String createConstructorParamters(ArrayList<String> params){
		StringBuffer buffer = new StringBuffer();
		
		Iterator<String> i = params.iterator();
		while(i.hasNext()){
			String[] temp = i.next().split("\\s");
			buffer.append(temp[0]).append(" ").append(temp[1]).append(" ").append(getTypeSpecificValue(temp[0])).append("\n");
		}
		return buffer.toString();
	}
	
	public String getParameterNamesList(ArrayList<String> params){
		StringBuffer buffer = new StringBuffer();

		Iterator<String> i = params.iterator();
		while(i.hasNext()){
			String[] temp = i.next().split("\\s");
			buffer.append(temp[1]);
			if(i.hasNext())
				buffer.append(",");
		}
		return buffer.toString();
	}
	
	public String getClassInstance(ArrayList<String> params, String method){
		StringBuilder buffer = new StringBuilder();
		
		if(classobj.getClassName().equals(method))
		{
			if (params != null && params.size() == 0)
				buffer.append(classobj.getClassName()).append(" ").append(fixtureobj).append("  = new ").append(classobj.getClassName()).append("();");
			else{
				buffer.append(createConstructorParamters(params));
				
				buffer.append(classobj.getClassName()).append(" ").append(fixtureobj).append("  = new ").append(classobj.getClassName()).append("(").append(getParameterNamesList(params)).append(");");
			}
		}else{
			if(classobj.getConstructors() != null && classobj.getConstructors().size() > 0){
				//check is there default constructor
				if(classobj.getDefaultConstructor() != null)
					buffer.append(classobj.getClassName()).append(" ").append(fixtureobj).append("  = new ").append(classobj.getClassName()).append("();");
				else{
					//if no default, take first constructor
					CConstructor ctemp = classobj.getConstructors().get(0);
					
					buffer.append(createConstructorParamters(ctemp.getParams()));
					buffer.append(classobj.getClassName()).append(" ").append(fixtureobj).append("  = new ").append(classobj.getClassName()).append("(").append(ctemp.getParameterNamesList()).append(");");
				}
			}else
				buffer.append(classobj.getClassName()).append(" ").append(fixtureobj).append("  = new ").append(classobj.getClassName()).append("();");
		}
		return buffer.toString();
	}
	
	@Override
	public String callClassMethod() {
		StringBuffer returnstring = new StringBuffer();		
		String parameterstring=null;
		
		if(cmethod!=null)
		{
			if(cmethod.getParametersCount()>0)
			{	
				parameterstring = getMethodParameter();
				returnstring.append(parameterstring).append("\n");
			}	
			
			if(cmethod.getMethodReturnType().equals("void"))
			{
				returnstring.append(fixtureobj).append(".").append(cmethod.getMethodName()).append("(").append(cmethod.getParameterNamesList()).append(");");
			}
			else 
			{	
				if(paramsList != null && paramsList.size() > 0){
					returnstring.append(cmethod.getMethodReturnType()).append(" ").append(resultobj).append(" = ").append(fixtureobj).append(".").append(cmethod.getMethodName()).append("(").append(this.getParameterNamesList(paramsList)).append(");\n");
				}else{
					returnstring.append(cmethod.getMethodReturnType()).append(" ").append(resultobj).append(" = ").append(fixtureobj).append(".").append(cmethod.getMethodName()).append("(").append(cmethod.getParameterNamesList()).append(");\n");
				}
		
				if(methodTestData == null || methodTestData.getExpected() == null||methodTestData.getExpected().isEmpty() )
					returnstring.append(cmethod.getMethodReturnType()).append(" ").append(expected).append(" ").append(getTypeSpecificValue(cmethod.getMethodReturnType()));
				
				else
					returnstring.append(cmethod.getMethodReturnType()).append(" ").append(expected).append(" ").append(" = "+methodTestData.getExpected()+";");
				
			}
		}
		else
			return null;
		
		return returnstring.toString();
		
	}

	private String getPremitivieType(String type){
				
		if(type.equals(CerberusConstants.PremitiveTypes.byteType.toString()))
			return " = 0;";
		
		else if(type.equals(CerberusConstants.PremitiveTypes.shortType.toString()))
			return " = 0;";
		
		else if(type.equals(CerberusConstants.PremitiveTypes.intType.toString()))
			return " = 0;";
		
		else if(type.equals(CerberusConstants.PremitiveTypes.doubleType.toString()))
			return " = 0.0;";
		
		else if(type.equals(CerberusConstants.PremitiveTypes.longType.toString()))
			return " = 0L;";
		
		else if(type.equals(CerberusConstants.PremitiveTypes.floatType.toString()))
			return  " = 0.0f;";
		
		else if(type.equals(CerberusConstants.PremitiveTypes.charType.toString()))
			return " = ' ';";
		
		else if(type.equals(CerberusConstants.PremitiveTypes.stringType.toString()))
			return " = null;";
		
		else if(type.equals(CerberusConstants.PremitiveTypes.booleanType.toString()))
			return " = false;";
		else 
			return " = null;";
	
	}
	
	private String getWrapperType(String type){
		if(type.equals(CerberusConstants.WrapperTypes.byteType.toString()))
			return " = new Byte((byte)0);";
		else if(type.equals(CerberusConstants.WrapperTypes.shortType.toString()))
			return " = new Short((short)0);";
		else if(type.equals(CerberusConstants.WrapperTypes.integerType.toString()))
			return " = new Integer(0);";
		else if(type.equals(CerberusConstants.WrapperTypes.doubleType.toString()))
			return " = new Double(0.0);";
		else if(type.equals(CerberusConstants.WrapperTypes.longType.toString()))
			return " = new Long(0L);";
		else if(type.equals(CerberusConstants.WrapperTypes.floatType.toString()))
			return " = new Float(0.0f);";
		else if(type.equals(CerberusConstants.WrapperTypes.charType.toString()))
			return " = new Character(' ');";
		else if(type.equals(CerberusConstants.WrapperTypes.booleanType.toString()))
			return " = new Boolean(false);";
		else	
			return null;
	}
	
	protected String getTypeSpecificValue(String type)
	{
		if(this.getPremitivieType(type) != null){
			return getPremitivieType(type);
		}else
			return getWrapperType(type);
	}
	
	
	protected String getMethodParameter()
	{			
		StringBuffer buffer = new StringBuffer();
		paramsList = new ArrayList<String>();
		ArrayList<String> methodTestDataParams= new ArrayList<String>();
		if(methodTestData != null)
			methodTestDataParams=methodTestData.getParam();
		for (int paramcount = 0; paramcount<cmethod.getParametersCount(); paramcount++){
			//check is there any constructor and no default constructor
			if(classobj.getConstructors().size() > 0 && classobj.getDefaultConstructor() == null){
				CConstructor temp = classobj.getConstructors().get(0);
				
				if(temp.getParameterNamesList().contains(cmethod.getParamterName(paramcount))){
					buffer.append(cmethod.getParamters(paramcount)).append("1 ").append(getTypeSpecificValue(cmethod.getParameterType(paramcount)));
					paramsList.add(cmethod.getParamters(paramcount)+"1");
				}
				else{
					paramsList.add(cmethod.getParamters(paramcount));
					buffer.append(cmethod.getParamters(paramcount)).append(" ").append(getTypeSpecificValue(cmethod.getParameterType(paramcount)));
				}
			}else{
				if(methodTestData==null || methodTestDataParams.get(paramcount).equals(null) ||methodTestDataParams.get(paramcount).isEmpty() )
					buffer.append(cmethod.getParamters(paramcount)).append(" ").append(getTypeSpecificValue(cmethod.getParameterType(paramcount)));
				else
					buffer.append(cmethod.getParamters(paramcount)).append(" ").append(" = "+methodTestDataParams.get(paramcount) +";");

			}
		}
		return buffer.toString();
	}
	
	//main method for creating of method body
	//Lin - add methodSignature
	public StringBuffer createMethodBody(JUnitMethodCheck assertionCheck, StringBuffer buffer, String delimiter,String method,ArrayList<String> params, String methodSignature,TestGenerationData testGenData)
	{		 
		if(testGenData != null){
			if(params.size()!=0)
				this.methodTestData= testGenData.getMethodTestData(method,params);
			else
				this.methodTestData= testGenData.getMethodTestData(method);
		}
			buffer.append(getClassInstance(params,method)).append("").append(delimiter); 
			//whether constructor or not
			if(classobj.getClassName().equals(method))
			{	
				buffer.append(this.assertNotNull(fixtureobj)).append("").append(delimiter);
			}			
			else{
				
				this.cmethod = getSpecificMethod(method, methodSignature);
			
				buffer.append(callClassMethod()).append("").append(delimiter);				
			
				if(cmethod!=null && !cmethod.getMethodReturnType().equals("void")) {
					if(assertionCheck.isAssertExists(CerberusConstants.ASSERTNOTNULL))
						buffer.append(this.assertNotNull(resultobj)).append("").append(delimiter);
					if(assertionCheck.isAssertExists(CerberusConstants.ASSERTEQUALS))
						buffer.append(this.assertEquals(cmethod.getMethodReturnType(), resultobj, expected)).append("").append(delimiter);
					if(assertionCheck.isAssertExists(CerberusConstants.ASSERTTRUE))
						buffer.append(this.assertTrue (cmethod.getMethodReturnType(), resultobj, expected)).append("").append(delimiter);
					if(assertionCheck.isAssertExists(CerberusConstants.ASSERTFALSE))
						buffer.append(this.assertFalse(cmethod.getMethodReturnType(), resultobj, expected)).append("").append(delimiter);
				
				}
			}
			return buffer;
	}

	//Lin - add methodSignature
	protected CMemberMethod getSpecificMethod(String methodName, String methodSignature){	
		
		if(classobj.getMethod(methodName)!=null)
		{	
			ArrayList<CMemberMethod> temp = new ArrayList<CMemberMethod>();
			temp = classobj.getMethod(methodName);
			if(temp.size() == 1){
				return temp.get(0);
			}else{
				
				for (CMemberMethod tempmethod:temp){
					
					if(tempmethod.getSignature().equalsIgnoreCase(methodSignature))
						return tempmethod;
				}
			}
		}
		return null;
	}
	
	public void setCMethod(CMemberMethod method){
		this.cmethod = method;
	}
}
